package com.janetfilter.core;

import com.janetfilter.core.attach.VMLauncher;
import com.janetfilter.core.attach.VMSelector;
import com.janetfilter.core.commons.DebugInfo;
import com.janetfilter.core.utils.WhereIsUtils;

import java.io.File;
import java.lang.instrument.Instrumentation;
import java.net.URI;
import java.util.jar.JarFile;

/**
 * 项目的主启动类，作为整个框架的入口点。
 * 支持三种方式启动：
 * 1. 直接运行jar (main方法)
 * 2. 作为Java Agent启动 (premain方法)
 * 3. 动态Attach到运行中的Java进程 (agentmain方法)
 */
public class Launcher {
    // 命令行参数，用于指定Attach模式
    public static final String ATTACH_ARG = "--attach";
    // 当前框架版本
    public static final String VERSION = "2022.2.0";
    // 版本号，用于版本兼容性检查
    public static final int VERSION_NUMBER = 202201000;

    // 防止多次加载的标记
    private static boolean loaded = false;

    /**
     * 主方法，直接运行jar时的入口点
     * 
     * @param args 命令行参数
     */
    public static void main(String[] args) {
        URI jarURI;
        try {
            // 获取当前jar文件的URI
            jarURI = WhereIsUtils.getJarURI();
        } catch (Throwable e) {
            // 如果无法定位jar文件，则输出错误信息并退出
            DebugInfo.error("Can not locate `ja-netfilter` jar file.", e);
            return;
        }

        String jarPath = jarURI.getPath();
        // 检查是否是Attach模式
        if (args.length > 1 && args[0].equals(ATTACH_ARG)) {
            // 启动Attach模式，将agent附加到指定的Java进程
            VMLauncher.attachVM(jarPath, args[1], args.length > 2 ? args[2] : null);
            return;
        }

        // 显示使用信息
        printUsage();

        try {
            // 启动VM选择器界面，让用户选择要附加的Java进程
            new VMSelector(new File(jarPath)).select();
        } catch (Throwable e) {
            System.err.println("  ERROR: Select virtual machine failed.");
            e.printStackTrace(System.err);
        }
    }

    /**
     * Java Agent模式的入口点，当使用-javaagent参数启动JVM时，会调用此方法
     * 
     * @param args Java Agent的参数
     * @param inst Instrumentation实例，提供类重定义和转换的能力
     */
    public static void premain(String args, Instrumentation inst) {
        premain(args, inst, false);
    }

    /**
     * Attach模式的入口点，当动态附加到运行中的JVM时，会调用此方法
     * 
     * @param args Agent参数
     * @param inst Instrumentation实例
     */
    public static void agentmain(String args, Instrumentation inst) {
        // 如果没有设置debug级别，则设置默认为1
        if (null == System.getProperty("janf.debug")) {
            System.setProperty("janf.debug", "1");
        }

        // 如果没有设置输出方式，则设置默认为3(控制台+文件)
        if (null == System.getProperty("janf.output")) {
            System.setProperty("janf.output", "3");
        }

        // 调用通用的premain方法，标记为Attach模式
        premain(args, inst, true);
    }

    /**
     * 实际的初始化方法，由premain和agentmain调用
     * 
     * @param args Agent参数
     * @param inst Instrumentation实例
     * @param attachMode 是否为Attach模式
     */
    private static void premain(String args, Instrumentation inst, boolean attachMode) {
        // 检查是否已经加载过，防止多次加载
        if (loaded) {
            DebugInfo.warn("You have multiple `ja-netfilter` as javaagent.");
            return;
        }

        printUsage();

        URI jarURI;
        try {
            loaded = true;
            // 获取当前jar文件的URI
            jarURI = WhereIsUtils.getJarURI();
        } catch (Throwable e) {
            DebugInfo.error("Can not locate `ja-netfilter` jar file.", e);
            return;
        }

        File agentFile = new File(jarURI.getPath());
        try {
            // 将agent jar添加到引导类加载器的搜索路径中
            // 这样可以确保agent类对所有应用类可见
            inst.appendToBootstrapClassLoaderSearch(new JarFile(agentFile));
        } catch (Throwable e) {
            DebugInfo.error("Can not access `ja-netfilter` jar file.", e);
            return;
        }

        // 初始化环境，并启动框架
        Initializer.init(new Environment(inst, agentFile, args, attachMode));
    }

    /**
     * 打印使用信息和版本信息
     */
    private static void printUsage() {
        String content = "\n  ============================================================================  \n" +
                "\n" +
                "    ja-netfilter " + VERSION +
                "\n\n" +
                "    A javaagent framework :)\n" +
                "\n" +
                "    https://github.com/ja-netfilter/ja-netfilter\n" +
                "\n" +
                "  ============================================================================  \n\n";

        System.out.print(content);
    }
}
